﻿/*------------------------------------------------------------------------

wifstreambuf.h 1.0.2

Copyright 楊志賢 CxxlMan, 2012
All Rights Reserved

- 可以用 Unicode 檔名讀取 Unicode 文字檔
- 只能處理 win32 平台用的 UTF-16 Unicode
- 供 wistream 使用
- 只能用於 win32 平台，因使用 _wopen() 函數

------------------------------------------------------------------------*/


#ifndef WIFSTREAMBUF_H_INCLUDED
#define WIFSTREAMBUF_H_INCLUDED

#include <istream>
#include <io.h>
#include <fcntl.h>
#include <windows.h>

namespace CxxlMan
{

// 可以用 Unicode 檔名的 streambuf
class wifstreambuf:public std::wstreambuf
{
  int fd; // 保存由 _wopen() 開啟的檔案 handle

  unsigned m_bufsize;
  wchar_t *m_buffer;


  virtual int_type underflow()
  {
    int r = read(fd, m_buffer, m_bufsize * sizeof(*m_buffer));
    if(r == 0)
      return traits_type::eof();

    wchar_t *Start;

    if(*m_buffer == 0xfeff) // 若含有 BOM 標識
      Start = m_buffer + 1;
    else
      Start = m_buffer;

    wchar_t *End = m_buffer + r / sizeof(*m_buffer);

    if(Start >= End)
      return traits_type::eof();

    setg(Start, Start, End);
    return *gptr();
  }

  // seekg() 會叫用到，因只是做讀入用，所以不做 out 的處理
  virtual pos_type seekoff(off_type off, std::ios_base::seekdir dir,
                   std::ios_base::openmode which = std::ios_base::in)
  {
    pos_type pos =  lseek(
                        fd, off * sizeof(*m_buffer),
                        (dir ==  std::ios::beg) ? SEEK_SET :
                        (dir ==  std::ios::cur) ? SEEK_CUR :
                                                  SEEK_END
                      );

    if (pos < 0) return -1;

    setg(m_buffer, m_buffer + 1, m_buffer + 1); // 這樣可以強迫重讀
    return pos;
  }

  virtual std::streamsize xsgetn(char_type *dest, std::streamsize n)
  {
    int nread = 0;

    while (n > 0)
    {
      int avail = in_avail();
      if (avail == 0)
      {
        if(underflow() == traits_type::eof())
           break;
        avail = in_avail();
      }

      if(avail > n)
        avail = n;

      memcpy((char*)(dest + nread), (char*)gptr(), avail*sizeof(*dest));
      gbump(avail);

      nread += avail;
      n -= avail;
    }

    return nread;
  }

public:

  // Constructor
  wifstreambuf(const wchar_t *Filename)
  {
    fd = _wopen(Filename,_O_RDONLY | _O_BINARY);
    m_bufsize = 512;
    m_buffer = new wchar_t[512];

    // 一開始設 buffer 是空的
    setg(m_buffer, m_buffer + m_bufsize, m_buffer + m_bufsize);
  }

  // Destructor
  virtual ~wifstreambuf()
  {
    if(fd != -1)
      close(fd);

    delete [] m_buffer;
  }

  bool isOpen()
  {
    return fd != -1;
  }
};

}  /* namespace CxxlMan */
#endif // WIFSTREAMBUF_H_INCLUDED
